package com.greate.community.util;

import java.io.File;
import java.io.FileInputStream;
import java.util.*;

import com.alibaba.fastjson.JSONObject;
import com.tencent.cloud.*;
import com.tencent.cloud.cos.util.Jackson;

import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Data
@Component
public class CosStsClientUtil {

    @Value("${tencent.secretId}")
    private String secretId;

    @Value("${tencent.secretKey}")
    private String secretKey;

    @Value("${tencent.avatarBucketName}")
    private String avatarBucketName;

    @Value("${tencent.avatarRegionName}")
    private String avatarRegionName;

    @Value("${tencent.pictureBucketName}")
    private String pictureBucketName;

    @Value("${tencent.pictureRegionName}")
    private String pictureRegionName;

    @Value("${tencent.studentCardBucketName}")
    private String studentCardBucketName;

    @Value("${tencent.studentCardRegionName}")
    private String studentCardRegionName;

    @Value("${https.proxyHost:}")
    private String proxyHost;

    @Value("${https.proxyPort:}")
    private String proxyPort;

    /**
     * 基本的临时上传密钥申请，适合对一个桶内的一批对象路径，统一授予一批操作权限
     */
    public JSONObject getCredential(String type, String... allowPrefixes) {
        JSONObject result = new JSONObject();
        try {
            // 获取腾讯云配置
            TreeMap<String, Object> config = getBaseConfig(type);
            result.put("bucket", config.get("bucket"));
            result.put("region", config.get("region"));

            // /userid/*
            // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径
            // 列举几种典型的前缀授权场景：
            // 1、允许访问所有对象："*"
            // 2、允许访问指定的对象："a/a1.txt", "b/b1.txt"
            // 3、允许访问指定前缀的对象："a*", "a/*", "b/*"
            // 如果填写了“*”，将允许用户访问所有资源；除非业务需要，否则请按照最小权限原则授予用户相应的访问权限范围(使用通配符*存在重大安全风险, 请谨慎评估使用)
            config.put("allowPrefixes", allowPrefixes);

            // 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
            // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            String[] allowActions = new String[]{
                    // 简单上传
                    "name/cos:PutObject",
                    // 表单上传、小程序上传
                    "name/cos:PostObject",
                    // 分片上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload",
                    // 下载操作
                    "name/cos:GetObject"
            };
            config.put("allowActions", allowActions);

            result.put("credential", CosStsClient.getCredential(config));

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("no valid secret !");
        }
    }

    /**
     * 基本的临时下载密钥申请，适合对一个桶内的一批对象路径，统一授予一批操作权限
     */
    public JSONObject getDownloadCredential(String type, String... allowPrefixes) {
        JSONObject result = new JSONObject();
        try {
            // 获取腾讯云配置
            TreeMap<String, Object> config = getBaseConfig(type);
            result.put("bucket", config.get("bucket"));
            result.put("region", config.get("region"));

            // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径
            // 列举几种典型的前缀授权场景：
            // 1、允许访问所有对象："*"
            // 2、允许访问指定的对象："a/a1.txt", "b/b1.txt"
            // 3、允许访问指定前缀的对象："a*", "a/*", "b/*"
            // 如果填写了“*”，将允许用户访问所有资源；除非业务需要，否则请按照最小权限原则授予用户相应的访问权限范围(使用通配符*存在重大安全风险, 请谨慎评估使用)
            config.put("allowPrefixes", allowPrefixes);

            // 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
            // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            String[] allowActions = new String[]{
                    // 下载操作
                    "name/cos:GetObject"
            };
            config.put("allowActions", allowActions);

            result.put("credential", CosStsClient.getCredential(config));

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("no valid secret !");
        }
    }

    /**
     * 获取腾讯云COS基础配置，包括 SecretId、SecretKey、地域、桶名
     *
     * @param type 上传的类型
     */
    private TreeMap<String, Object> getBaseConfig(String type) {
        TreeMap<String, Object> config = new TreeMap<String, Object>();
        // 云 api 密钥 SecretId
        config.put("secretId", secretId);
        // 云 api 密钥 SecretKey
        config.put("secretKey", secretKey);

        if (StringUtils.isNotEmpty(proxyHost)) {
            System.setProperty("https.proxyHost", proxyHost);
            System.setProperty("https.proxyPort", proxyPort);
        }

        // 如果您使用了腾讯云 cvm，可以设置内部域名
//            config.put("host", "sts.internal.tencentcloudapi.com");

        // 临时密钥有效时长，单位是秒，默认 1800 秒，目前主账号最长 2 小时（即 7200 秒），子账号最长 36 小时（即 129600）秒
        config.put("durationSeconds", 1800);
        // 配置地域和桶名
        if (Objects.equals(type, "avatar")) {
            config.put("bucket", avatarBucketName);
            config.put("region", avatarRegionName);
        } else if (Objects.equals(type, "picture")) {
            config.put("bucket", pictureBucketName);
            config.put("region", pictureRegionName);
        } else if (Objects.equals(type, "studentCard")) {
            config.put("bucket", studentCardBucketName);
            config.put("region", studentCardRegionName);
        } else {
            throw new IllegalArgumentException("no valid type !");
        }
        return config;
    }

    /**
     * 基本的临时密钥申请示例，适用于对多个桶的对应文件路径设置分别多条权限
     */
    private void testGetCredential2() {
        TreeMap<String, Object> config = new TreeMap<String, Object>();

        try {
            Properties properties = new Properties();
            File configFile = new File("local.properties");
            properties.load(new FileInputStream(configFile));

            // 固定密钥 SecretId
            config.put("secretId", properties.getProperty("SecretId"));
            // 固定密钥 SecretKey
            config.put("secretKey", properties.getProperty("SecretKey"));

            if (properties.containsKey("https.proxyHost")) {
                System.setProperty("https.proxyHost", properties.getProperty("https.proxyHost"));
                System.setProperty("https.proxyPort", properties.getProperty("https.proxyPort"));
            }

            // 临时密钥有效时长，单位是秒
            config.put("durationSeconds", 1800);

            //设置 policy
            List<Scope> scopes = new ArrayList<Scope>();
            Scope scope = new Scope("name/cos:PutObject", "sts-sdk-test-1251668577", "ap-chengdu", "/test.txt");
            scopes.add(scope);
            scopes.add(new Scope("name/cos:GetObject", "sts-sdk-test-1251668577", "ap-chengdu", "/test.txt"));
            config.put("policy", CosStsClient.getPolicy(scopes));

            Response credential = CosStsClient.getCredential(config);
            System.out.println(Jackson.toJsonPrettyString(credential));
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("no valid secret !");
        }
    }

    /**
     * 更细致粒度设置的临时密钥申请示例，根据 https://cloud.tencent.com/document/product/598/10603 中列出的元素设置临时密钥权限
     */
    private void testGetCredential3() {
        TreeMap<String, Object> config = new TreeMap<String, Object>();

        try {
            Properties properties = new Properties();
            File configFile = new File("local.properties");
            properties.load(new FileInputStream(configFile));

            // 云 api 密钥 SecretId
            config.put("secretId", properties.getProperty("SecretId"));
            // 云 api 密钥 SecretKey
            config.put("secretKey", properties.getProperty("SecretKey"));

            if (properties.containsKey("https.proxyHost")) {
                System.setProperty("https.proxyHost", properties.getProperty("https.proxyHost"));
                System.setProperty("https.proxyPort", properties.getProperty("https.proxyPort"));
            }

            // 初始化 policy
            Policy policy = new Policy();
            policy.setVersion("2.0");

            // 开始构建一条 statement
            Statement statement = new Statement();
            // 声明设置的结果是允许操作
            statement.setEffect("allow");
            // 添加一批操作权限
            statement.addActions(new String[]{
                    "name/cos:PutObject",
                    "name/cos:PostObject",
                    // 分片上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload"
            });
            // 添加一批资源路径
            // 一条资源的规则是：qcs::cos:<REGION>:uid/<APPID>:<BUCKET-APPID>/<OBJECT>
            statement.addResources(new String[]{
                    String.format("qcs::cos:%s:uid/%s:%s/%s",
                            "ap-chengdu", "1251668577", "sts-sdk-test-1251668577", "exampleObject")
            });

            // 开始构建生效条件 condition
            // 关于 condition 的详细设置规则可以参考 https://cloud.tencent.com/document/product/598/10604#.E5.AD.97.E7.AC.A6.E4.B8.B2.E8.AF.B4.E6.98.8E
            ConditionTypeValue conditionTypeValue = new ConditionTypeValue();
            conditionTypeValue.setKey("qcs:ip");
            conditionTypeValue.addValue("10.10.10.10");
            conditionTypeValue.addValue("10.10.10.11");

            // 增加规则
            statement.addCondition("ip_equal", conditionTypeValue);

            // 把一条 statement 添加到 policy
            // 可以添加多条
            policy.addStatement(statement);

            // 临时密钥有效时长，单位是秒
            config.put("durationSeconds", 1800);
            // 换成 bucket 所在地区
            config.put("region", "ap-chengdu");

            // 将 Policy 示例转化成 String，可以使用任何 json 转化方式，这里是本 SDK 自带的推荐方式
            config.put("policy", Jackson.toJsonPrettyString(policy));

            Response response = CosStsClient.getCredential(config);
            System.out.println(Jackson.toJsonPrettyString(response));
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("no valid secret !");
        }
    }
}

